package com.qf.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.qf.feign.OrderServiceFeign;
import com.qf.pojo.R;
import com.qf.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/user")
@RefreshScope //在感知到配置变化后，将当前注解修饰的对象重建
public class UserController2 {

    @Value("${desc}")
    private String desc;

    @Autowired
    private OrderServiceFeign orderServiceFeign;

    /*
        这个通过blockHandler属性配置的方法
            要求①：返回值和参数列表必须和目标方法保持一致
            要求②：这个方法的参数列表最后一个参数必须是BlockException

        这个方法只能处理流控异常，对于非流控的异常，无法处理的。此时我们需要配置fallback属性
     */
    public R blockFallback(Integer id, BlockException e){
        e.printStackTrace();

        return R.error().putMsg(id + "访问，被流控了...");
    }

    /*
        这个通过fallback属性配置的方法
            要求：返回值和参数列表必须和目标方法保持一致
            在保障上述要求的前提下，这个方法的最后一个参数可给可不给，如果给，使用Throwable接收

        这个方法可以处理任意类型的异常，包括流控异常。如果没有设置blockHandler，所有的流控异常也会走这个方法。
    */
    public R fallback(Integer id,Throwable e){
        e.printStackTrace();
        return R.error().putMsg(id + "访问出现异常了...");
    }

    /*
        这个通过defaultFallback属性配置的方法
            要求：返回值必须和目标方法保持一致。参数不要求。
            在保障上述要求的前提下，这个方法的最后一个参数可给可不给，如果给，使用Throwable接收

        这个方法可以处理任意类型的异常，包括流控异常。如果没有设置blockHandler，所有的流控异常也会走这个方法。
    */
    public R defaultFallback(Throwable e){
        e.printStackTrace();
        return R.error().putMsg("服务器忙，稍后重试...");
    }

    @GetMapping("/{id}")
    // 这个注解标注的方法就是sentinel所保护的资源，这个注解可以添加在任何方法上（这个注解类似于Hystrix的 HystrixCommand注解的功能）
    @SentinelResource(value = "findOrders"/*,blockHandler = "blockFallback",fallback = "fallback"*/,defaultFallback = "defaultFallback")
    public R findOrders(@PathVariable("id") Integer id/*,@RequestHeader("") String ss*/) throws InterruptedException {

        //主要演示慢调用，触发熔断
        if (id == 1){
            Thread.sleep(1000);
        }

        //演示异常数、异常比例，触发熔断
        if (id == 2){
            throw  new RuntimeException("error");
        }

        R r = orderServiceFeign.findByUid(id);

        System.out.println("========================" + desc);
        return r;
    }

    //演示 关联模式的 流控模式
    @GetMapping("/test")
    @SentinelResource("test")
    public R test(){
        return R.success().putMsg("success~~");
    }

    //演示 链路模式的 流控模式

    @Autowired
    private UserService userService;

    @GetMapping("/test1")
    @SentinelResource("test1")
    public R test1() throws InterruptedException {
        userService.findById(1);
        return R.success().putMsg("test1 success~~");
    }

    @GetMapping("/test2")
    @SentinelResource("test2")
    public R test2() throws InterruptedException {
        userService.findById(1);
        return R.success().putMsg("test2 success~~");
    }

}
